# Р7 Офис

Произведен анализ PRM-пакетов программного изделия "Р7 Офис" актуальной на 06.06.2022 г. версии.

Целью анализа является выявление особенностей упаковки, препятствующих включению RPM-пакета Р7 Офиса в состав дистрибутива и/или репозиторий М ОС.

## Объект тестирования

Были скачаны пакеты для ALT Linux и ROSA Linux с сайта: [https://r7-office.ru/downloads/](https://r7-office.ru/downloads/)

Артефакт №1:

* пакет [https://download.r7-office.ru/altlinux/r7-office.rpm](https://download.r7-office.ru/altlinux/r7-office.rpm)
* хеш MD5: `d2a41daf17b84d3531f70c53e41ca930`
* Дальнейшее имя файла: `r7-office-altlinux.rpm`
* заявленное назначение: пакет для ALT Linux
* версия и релиз пакета (`rpm -qp --qf '%{version}-%{release}\n' r7-office-altlinux.rpm.rpm`): 7.0.2-5

Артефакт №2:

* пакет [https://download.r7-office.ru/redos/r7-office.rpm](https://download.r7-office.ru/redos/r7-office.rpm)
* хеш MD5: `a9cd670cc3a192b162b14a19aec00f5c`
* Дальнейшее имя файла: `r7-office-rosa.rpm`
* заявленное назначение: пакет для ROSA Linux
* версия и релиз пакета (`rpm -qp --qf '%{version}-%{release}\n' r7-office-rosa.rpm`): 7.0.2-5

Примечание: в ссылке на пакет написано redos, однако, судя по зависимостям, это пакет именно для ROSA Fresh/Chrome. RedOS — это другой дистрибутив. Под брендом ROSA также выпускаются дистрибутивы на основе CentOS (ROSA Cobalt), для них эти пакеты не подойдут. Ниже написано, как можно сделать зависимости универсальными, чтобы пакеты могли подходить и для ROSA Fresh/Chrome, и для ROSA Cobalt, и для ALT Linux и RedOS.

## Анализ пакетов

### Анализ скриптлетов пакетов

В обоих пакетах содержатся одинаковые скриптлеты — скрипты, выполняемые при установке и/или удалении. Зафисируем их идентичность в обоих пакетах:

```
$ rpm -qp --scripts r7-office-rosa.rpm | md5sum
e7e3b9dff41ce229796c7c1eef5eae1c  -
$ rpm -qp --scripts r7-office-altlinux.rpm | md5sum
e7e3b9dff41ce229796c7c1eef5eae1c  -
```

Ниже приведен код скриптлетов полностью и без изменений:

```
$ rpm -qp --scripts r7-office-rosa.rpm
preinstall program: /bin/sh
postinstall scriptlet (using /bin/sh):

set -e 		# fail on any error
set -u 		# treat unset variable as errors

# Add icons to the system icons
XDG_ICON_RESOURCE="`which xdg-icon-resource 2> /dev/null || true`"
if [ ! -x "$XDG_ICON_RESOURCE" ]; then
  echo "Error: Could not find xdg-icon-resource" >&2
  exit 1
fi
for icon in "/opt/r7-office/desktopeditors/asc-de-"*.png; do
  size="${icon##*/asc-de-}"
  "$XDG_ICON_RESOURCE" install --size "${size%.png}" "$icon" "r7-office"
done

UPDATE_MENUS="`which update-menus 2> /dev/null || true`"
if [ -x "$UPDATE_MENUS" ]; then
  update-menus
fi

MIMEAPPS_LIST="/usr/share/applications/mimeapps.list"
if [ ! -f "$MIMEAPPS_LIST" ]; then
  echo "[Default Applications]" >"$MIMEAPPS_LIST"
fi
if [ $(cat "$MIMEAPPS_LIST" | grep x-scheme-handler/oo-office | wc -l) -eq "0" ]; then
  echo "x-scheme-handler/oo-office=r7-office-desktopeditors.desktop" >>"$MIMEAPPS_LIST"
fi
if [ $(cat "$MIMEAPPS_LIST" | grep text/docxf | wc -l) -eq "0" ]; then
  echo "text/docxf=r7-office-desktopeditors.desktop" >>"$MIMEAPPS_LIST"
fi
if [ $(cat "$MIMEAPPS_LIST" | grep text/oform | wc -l) -eq "0" ]; then
  echo "text/oform=r7-office-desktopeditors.desktop" >>"$MIMEAPPS_LIST"
fi

xdg-mime install --mode system /opt/r7-office/desktopeditors/mimetypes/onlyoffice-docxf.xml
xdg-mime install --mode system /opt/r7-office/desktopeditors/mimetypes/onlyoffice-oform.xml

# Update cache of .desktop file MIME types. Non-fatal since it's just a cache.
#update-desktop-database > /dev/null 2>&1 || true
preuninstall scriptlet (using /bin/sh):

set -e

action="$1"
if [ "$2" = "in-favour" ]; then
  # Treat conflict remove as an upgrade.
  action="upgrade"
fi
# Don't clean-up just for an upgrade.`
if [ "$action" = "upgrade" ] ; then
  exit 0
fi

# Remove icons from the system icons
XDG_ICON_RESOURCE="`which xdg-icon-resource 2> /dev/null || true`"
if [ ! -x "$XDG_ICON_RESOURCE" ]; then
  echo "Error: Could not find xdg-icon-resource" >&2
  exit 1
fi
for icon in "/opt/r7-office/desktopeditors/asc-de-"*.png; do
  size="${icon##*/asc-de-}"
  "$XDG_ICON_RESOURCE" uninstall --size "${size%.png}" "r7-office"
done

UPDATE_MENUS="`which update-menus 2> /dev/null || true`"
if [ -x "$UPDATE_MENUS" ]; then
  update-menus
fi

# Update cache of .desktop file MIME types. Non-fatal since it's just a cache.
#update-desktop-database > /dev/null 2>&1 || true
postuninstall scriptlet (using /bin/sh):

set -e 		# fail on any error
```

Вызывается утилита which, однако ее нет в зависимостях пакета ("`Requires(post): which`"). Она не обязана быть установлена.

Выполняется копирования иконок скриптом. Типовая практика — положить иконки уже внутри пакета сразу в `/usr/share/icons/hicolor` или иное место в `/usr/share`. Считаем важным избежать создания файлов в обход пакетной системы.

Выполняется редактирование чужого (не принадлежащего этому пакету) файла `/usr/share/applications/mimeapps.list`. Подобные вмешательства в ОС недопустимы. Типовая практика следующая: в `/usr/share/applications/*.desktop` указываются поддерживаемые MIME-типы, а дальше система (графическая оболочка) разбирается сама.

Если ставить свои MIME-типы, то их тоже нужно по возможности положить не в обход пакетной системы.

Выполняется установка своих MIME-типов. Рассмотрим, как это работает. Создаем файл `/tmp/123.docx` в любом офисном пакете. До установки подобного пакета смотрим, каков его тип по мнению системы:

```
$ xdg-mime query filetype /tmp/123.docx
application/vnd.openxmlformats-officedocument.wordprocessingml.document
```

После вмешательства скрипта от R7 MIME-типом будет не `application/vnd.openxmlformats-officedocument.wordprocessingml.document`, а придуманный R7 свой тип. Введение подобных типов приводит к следующим последствиям:

* Некоторые приложения, в т.ч. веб, например, web.whatsapp.com и telegram-desktop тоже, определяют MIME-тип прикрепляемых файлов на клиенте. В результате получатель отправления будет видеть вложение не как docx-документ, а как файл неизвестного типа.
* В формате odf в первые байты zip-архива записывается MIME-тип. В docx, который тоже является zip-архивом, такого не делается. После введения своих MIME-типов для docx некоторые или многие программы могут начать определять docx как zip (см. [https://wiki.archlinux.org/title/WPS_Office#WPS_Office_Overriding/Breaks_Mime](https://wiki.archlinux.org/title/WPS_Office#WPS_Office_Overriding/Breaks_Mime))
* Если файловый менеджер начнёт считать `*.docx` (и `*.xlsx`) особыми типами исключительно для R7, то он перестанет предлагать LibreOffice и др. офисные пакеты в разделе "Открыть с помощью".

`update-desktop-database` выполняется автоматически файловыми триггерами системных пакетов и заносит в кеш ассоциации `/usr/share/applications/*.desktop` и прописанных в них MIME-типов (скрипт пишет их в `mimeapps.list`, хотя это файл для указания приоритетов ассоциаций, а не фактов их наличия; когда как только лишь одна программа умеет открывать особый тип, заносить его в файл для приоритетов ассоциаций смысла нет, т.к. есть всего лишь один вариант).

Просим убрать свои MIME-типы и указать стандартные в списке поддерживаемых в desktop-файлах.

### Анализ зависимостей пакетов

Собирается несколько разных PRM-пакетов, которые отличаются только лишь зависимостями. В RPM существует механизм универсальных зависимостей, он описан, например, здесь: [https://nixtux.ru/1124](https://nixtux.ru/1124).

Зависимости анализируемых пакетов:

```
$ rpm -qpR r7-office-altlinux.rpm | sort -u
/bin/sh
curl
fonts-ttf-dejavu
fonts-ttf-google-crosextra-carlito
fonts-ttf-liberation
libatk
libcairo
libgtk+3
libX11
libXScrnSaver
rpmlib(CompressedFileNames) <= 3.0.4-1
rpmlib(FileDigests) <= 4.6.0-1
rpmlib(PayloadFilesHavePrefix) <= 4.0-1
rpmlib(PayloadIsXz) <= 5.2-1
xdg-utils
```

```
$ rpm -qpR r7-office-rosa.rpm | sort -u
/bin/sh
curl
fonts-ttf-dejavu
fonts-ttf-liberation
lib64atk1.0_0
lib64cairo2
lib64gtk+2.0_0
lib64x11_6
lib64xscrnsaver1
rpmlib(CompressedFileNames) <= 3.0.4-1
rpmlib(FileDigests) <= 4.6.0-1
rpmlib(PayloadFilesHavePrefix) <= 4.0-1
rpmlib(PayloadIsXz) <= 5.2-1
xdg-utils
```

Зависимости разные, а хеш-суммы файлов внутри пакетов совпадают: команда `diff -u --color <(rpm -qp --dump r7-office-altlinux.rpm | awk '{print $NF}' | sort -u) <(rpm -qp --dump r7-office-rosa.rpm | awk '{print $NF}' | sort -u)` ничего не выводит, из чего можно сделать предположение, что 2 разных варианта только лишь ради разных зависимостей.

Если вместо `lib64cairo2` и `libcairo` написать: `libcairo.so.2()(64bit)`, — то зависимость будет удовлетворяться во всех RPM-дистрибутивах.

Пакеты `curl` и `xdg-utils` в ALT Linux и ROSA называются одинаково, однако можно написать и так: `/usr/bin/xdg-icon-resource`, `/usr/bin/xdg-mime`. Такие зависимости сработают в ALT Linux всех последних версиях, в ROSA Fresh/Chrome платформы rosa2021.1 (версии 12 и новее), в актуальных версиях Fedora/Centos. На dnf из rosa2021.1 команда `sudo dnf --repofrompath redos,http://repo.red-soft.ru/redos/7.3/x86_64/os/ --disablerepo '*' --enablerepo redos install /usr/bin/xdg-mime` предлагает установить их xdg-utils, в новых redos уже не yum, а dnf, так что тоже должно работать.

Необходимо проверить необходимость зависимости от curl, то есть `/usr/bin/curl`. Возможно, стоит заменить на `libcurl.so.4()(64bit)`. `/usr/bin/curl`, строго говоря, не обязан быть слинкован с именно `libcurl.so.4`, в ROSA, например, [собирается](https://abf.io/import/curl/tree/rosa2021.1) `/usr/bin/curl-libressl`, слинкованный с `libcurl-libressl.so.4`, который мог бы быть `/usr/bin/curl`. ПРосьба прописать в зависимость библиотеку так, как указано в зависимостях ELF-файлов.

### Анализ содержимого пакетов

#### Скрипты запуска

Скрипты запуска в `/usr/bin/`, в частности `/usr/bin/r7-office-desktopeditors`.

Ставится переменная окружения `LD_LIBRARY_PATH`. Представим, что пользователь включил компьютер, открыл Офис и нажал на гиперссылку в документе. Открылся веб-браузер. Браузер унаследовал эту переменную окружения. Если браузер использует библиотеку `libfoo.so.0`, которая есть и в `/usr/lib64`, и в папках R7, то будет использована R7 библиотека вместо системной, что приведёт к непредсказуемым последствиям. Это проблема актуальна уже сейчас: если браузер Qupzilla, то будет использоваться Qt5 от R7 VLC вместо системного, а если браузер не на Qt, например, Chromium, но пользователь скачает какой-то файл, который откроется из браузера в Qt-программе, то произойдёт то же самое. Необходимо заменить LD_LIBRARY_PATH на RUNPATH (RPATH) в ELF-файлах. Про работу RPATH на этапе запуска программы можно почитать в `man ld-linux`, а задать его можно при компиляции (линковке) или с помощью утилиты `patchelf` (лучше на этапе компиляции/линковки).

Рекомендуется проверить код скриптом с помощью shellcheck. Он предложит в них расставить кавычки, заменить `wc -l` на `grep -c .` и т.д. shellcheck можно запускать на сайте [https://www.shellcheck.net/](https://www.shellcheck.net/) или даже внутри IDE. Например, так:

```
cd /tmp
wget https://github.com/koalaman/shellcheck/releases/download/v0.8.0/shellcheck-v0.8.0.linux.x86_64.tar.xz
tar -xvf shellcheck-v0.8.0.linux.x86_64.tar.xz
sudo install -m0755 shellcheck-v0.8.0/shellcheck /usr/local/bin/shellcheck
```

Затем можно открыть скрипт в Geany и выбирать в меню: Сборка —> Lint. Ниже скриншот результата. Можно вбить номера ошибок SC* в поисковик и почитать про них подробные объяснения.

![""](img/2022-06-06_10-29.png "")

На данный момент делается так: `eval TEMPLATE_DIR=$(grep XDG_TEMPLATES_DIR $HOME/.config/user-dirs.dirs | cut -d \" -f2)`. eval на непредсказуемые входные данные, прочитанные из пользовательского текстового файла, делать недопустимо. Пример сценария:

* устанавливается r7-office;
* включается IMA ([ROSA](http://wiki.rosalab.ru/ru/index.php/IMA), [ALT](https://www.altlinux.org/IMA_EVM)) — разрешается запуск всех исполняемых файлов, имевшихся в системе на момент фиксирования её состояния, а запуск всех остальных запрещается;
* `/usr/bin/r7-*` становятся доверенными, подписанными скриптами;
* средствами SELinux пользователю запрещено открывать эмулятор терминала (необязательно);
* пользователь, которому запрещено выполнять неподписанные файлы, пишет специальный текст в доступный ему на редактирование файл `$HOME/.config/user-dirs.dirs`;
* R7 скрипт выполняет это как команду;
* таким образом, проблемы скрипта R7 позволяют выполнить произвольный код в обход установленных ограничений.

Если опустить eval, то код все равно содержит проблемы. Во-первых, вместо ~/.config может использоваться иной путь, см. про `$XDG_CONFIG_HOME` в [https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html). Во-вторых, этого файла может не существовать. В-третьих, в комплекте ПО, которое создает этот файл, есть утилита для запроса необходимого значения штатным способом без grep: `xdg-user-dir TEMPLATES`. В случае использования такой записи необходимо добавить `xdg-user-dirs` или `/usr/bin/xdg-user-dir` в зависимости пакета или необходимо, чтобы код учитывал возможное отсутствие этой утилиты (и тогда, вероятно, вообще не стоит пытаться скопировать шаблоны).

`/opt/r7-office/desktopeditors/chrome-sandbox` имеет права 0644. Это  исполняемый файл. Вероятно, этот файл вообще не используется и его можно убрать из пакета?

Проблем с набором сбандленных библиотек не выявлено.

RPATH выглядит плохо:

```
$ patchelf --print-rpath VideoPlayer
$ORIGIN:$ORIGIN/system:$ORIGIN/common:/usr/local/Qt-5.9.8/gcc_64/lib
```

`/usr/local/Qt-5.9.8/gcc_64/lib` не существует за пределами сборочной среды R7.
